#include "partage.h"

/*
 * inode pointer list with all the files;
 */
typedef inode *inodew;
inodew list[MAXARG];

/*
 * used with select to wait for clients 
 * connection
 */
fd_set fds;

/*
 * this vector contains the current users 
 * connected to <<partage>>;
 *
 */
linkReseau users[MAXUSERS_PARTAGE];

/*
 * int nusersm describes the maximum index 
 * possible to have users on the vector users, 
 * there should not be a moment when nusersm 
 * is greather than MAXUSERS_PARTAGE
 */
int nusersm;

int main(int argc, char** argv){

	nusersm = 0;
	initFileSystem();
	initLinks();
	/* Create Clients */

	initUser(&(users[findFirstLinkNullAndIterate()]),"root",TYPE_utilisateur,PERM_W|PERM_R|PERM_L);
	initUser(&(users[findFirstLinkNullAndIterate()]),"util2",TYPE_utilisateur,PERM_W|PERM_R|PERM_L);
	initUser(&(users[findFirstLinkNullAndIterate()]),"encod1",TYPE_encodeur,PERM_W|PERM_R|PERM_L);
	initUser(&(users[findFirstLinkNullAndIterate()]),"encod2",TYPE_encodeur,PERM_W|PERM_R|PERM_L);
	initUser(&(users[findFirstLinkNullAndIterate()]),"ged1",TYPE_ged,PERM_W|PERM_R|PERM_L);
	initUser(&(users[findFirstLinkNullAndIterate()]),"cap1",TYPE_capture,PERM_W|PERM_R|PERM_L);
	
	waitForUserContact();
	exit(EXIT_SUCCESS);
}

void initLinks(){
	int i;
	for(i=0; i< MAXUSERS_PARTAGE;i++){
		users[i].tuFF[0] = -1;
		users[i].tuFF[1] = -1;
		users[i].streamFF = NULL; /* From Father */
		users[i].tuTF[0] = -1;
		users[i].tuTF[1] = -1;
		users[i].streamTF = NULL; /* To   Father */
		users[i].droits = 0;
		users[i].ownerId = 0;
		users[i].ownerName=NULL;
	}
}


int findFirstLinkNullAndIterate(){
	int i;
	for(i = 0;i<nusersm;i++){
		if(users[i].streamFF == NULL && users[i].streamTF == NULL)
			return i;
	}
	nusersm++;
	return i;
}
void cleanTu(){
	int i;
	for(i = 0;i<nusersm;i++){
		if(users[i].streamFF != NULL || users[i].streamTF != NULL){
			close(users[i].tuFF[TU_WRITE]);
			close(users[i].tuTF[TU_READ] );
		}
	}
}

int  initUser(linkReseau* user,char* name,int type,int perm){
	char tuR[30];
	char tuW[30];
	int pid;

	/*user->ownerName = name;*/

	user->ownerName = (char*)malloc(strlen(name));
	strcpy(user->ownerName,name);
	if(type >= sizeof(typenames))
		return PROCESS_ERROR;
	if(pipe(user->tuFF) == -1 || pipe(user->tuTF) == -1 ){
		perror("pipe");
		return PROCESS_ERROR;
	}
	switch( pid =  fork() ){
		case -1: 
			return PROCESS_ERROR;
			break;

		case 0: /* son */
			sprintf(tuR,"%d",user->tuFF[TU_READ ]);
			sprintf(tuW,"%d",user->tuTF[TU_WRITE]);
			cleanTu();
			logMessage(stdout, "Running  Name:%7s; Type:%17s; tuR:%s; tuW:%s\n",name,typenames[type],tuR,tuW);
			execlp(XTERM,XTERM,"-T",name,"-e",typenames[type],tuR,tuW,NULL);
			logMessage(stderr, "Could not run xterm, commands where: %s %s %s %s %s\n",XTERM,"-e",typenames[type],tuR,tuW);
			return PROCESS_ERROR;
		default: /* father */
			close( user->tuTF[TU_WRITE]);
			close( user->tuFF[TU_READ]); 
			user->ownerId = pid;
			break;
	}
	user->streamFF = fdopen(user->tuFF[TU_WRITE],"w"); 
	user->streamTF = fdopen(user->tuTF[TU_READ ],"r"); 
	if(user->streamFF == NULL || user->streamTF == NULL){
		if(user->streamFF == NULL){
			logMessage(stderr ,"Impossible to open the descriptor streamFF !\n");
			fclose(user->streamFF ); 
			close( user->tuFF[TU_WRITE]);
		}
		if(user->streamTF == NULL){
			logMessage( stderr ,"Impossible to open the descriptor streamTF, parameters:%d %d !\n",user->tuTF[TU_READ ],TU_READ );
			fclose(user->streamTF );
			close( user->tuTF[TU_READ]);
		}
		return PROCESS_ERROR;
	}

	return PROCESS_FATHER ;
}


int max(const int a, const int b){
	return (a>b)?a:b;
}

void waitForUserContact(){
	int i,ma;
	int retval ;
	int ttmp;
	char retbuf [ MAXSIZE ];
	char op[100];

	char typetmp;
	char* sendList = NULL;
	int flagUserDown;

	while (1) {
		FD_ZERO (&fds);
		ma = -1;
		for(i=0; i<nusersm;i++){
			if(users[i].streamTF == NULL)
				continue;
			if(feof(users[i].streamTF)){
				close(users[i].tuTF[TU_READ]);
				users[i].streamTF = NULL;
				continue;
			}
			FD_SET(users[i].tuTF[TU_READ],&fds);
			ma =  max(users[i].tuTF[TU_READ],ma);
		}
		if(ma == -1){
			logMessage(stdout,"No users connected, will wait for more %d second\n",PARTAGE_WAIT_TIME_WHEN_IDLE);
			sleep(PARTAGE_WAIT_TIME_WHEN_IDLE);
			continue;
		}
		select( ma+1 , &fds , NULL , NULL , NULL );
		for(i=0; i<nusersm;i++){
			if( users[i].streamTF == NULL){
				/* logMessage(stderr,"No user connected to virtual port %d\n",i);*/
				continue;
			}
			if ( FD_ISSET ( users[i].tuTF[TU_READ] , &fds )) {
				if(users[i].ownerName == NULL || feof(users[i].streamTF)){
					if(users[i].ownerName == NULL){
						logMessage(stderr,"User caused EOF: uid %d\n",i);
					}else{
						logMessage(stderr,"User caused EOF: uid %s\n",users[i].ownerName);
					}
					continue;
					
				}
				while(1){
					retval = read_and_parse ( users[i].streamTF , retbuf );
					flagUserDown = 0;
					if(retval < 0){
						switch(retval){
							case ARG_FIM_DE_STREAM:
								flagUserDown = 1;				
								break;
							case ARG_ERR_STREAM:
								break;
							case ARG_END_STREAM:
								break;
						}
						break;
					}
					if ( retval < 0) {
						exit ( EXIT_FAILURE );
					}
					sscanf(retbuf," %s",op);

					if(strcmp(op,"LST") == 0){
						getList(&sendList);
						fprintf(users[i].streamFF,"%s\n",sendList);
						free(sendList);
					}else if (strcmp(op,"ADD") == 0){
						sscanf(retbuf,"%*s %s",op);
						if(addVideo(op) == NULL){
							logMessage(stderr,"Could not create the file: \"%s\", request was from user %s\n",op,users[i].ownerName);
							fprintf(users[i].streamFF,"%s\n",ANSWER_FAILURE);
						}else{
							logMessage(stdout,"Succesfully added file \"%s\" to HardDisk, as requested from %s\n",op,users[i].ownerName);
							fprintf(users[i].streamFF,"%s\n",ANSWER_OK);
						}
					}else if (strcmp(op,"DEL") == 0){
						sscanf(retbuf," %*s %s",op);
						if(!removeVideo(op)){
							logMessage(stderr,"Could not remove file: \"%s\", request was from user %s\n",op,users[i].ownerName);

							fprintf(users[i].streamFF,"%s\n",ANSWER_FAILURE);
						}else{
							logMessage(stdout,"Succesfully removed file \"%s\" from HardDisk, as requested from %s\n",op,users[i].ownerName);
							fprintf(users[i].streamFF,"%s\n",ANSWER_OK);
						}
					}else if (strcmp(op,"ALL") == 0){

						getClientList(&sendList);
						fprintf(users[i].streamFF,"%s\n",sendList);
						free(sendList);

					}else if (strcmp(op,"REG") == 0){
						sscanf(retbuf," %*s %c %s",&typetmp,op);
						ttmp = -1;
						switch(typetmp){	
							case 'G':
								ttmp = TYPE_ged;
								break;
							case 'U':
								ttmp = TYPE_utilisateur;
								break;
							case 'C':
								ttmp = TYPE_capture;
								break;
							case 'E':
								ttmp = TYPE_encodeur;
								break;
							default:
								break;
						}
						if(ttmp == -1)
							break;
						if(i!= 0 || initUser(&(users[findFirstLinkNullAndIterate()]),op,ttmp,PERM_W|PERM_R|PERM_L) !=  PROCESS_FATHER){
							logMessage(stderr,"Could not init client: \"%s\", request was from user %s\n",op,users[i].ownerName);
							fprintf(users[i].streamFF,"%s\n",ANSWER_FAILURE);
						}else{
							logMessage(stdout,"Succesfully initiated client \"%s\", as requested from %s\n",op,users[i].ownerName);
							fprintf(users[i].streamFF,"%s\n",ANSWER_OK);
						}
					}else if (strcmp(op,"UNR") == 0){
						sscanf(retbuf," %*s %s",op);
						if(i!=0 || !removeClient(op)){
							if(users[i].streamFF == NULL){
								logMessage(stderr,"User %s successfully commited suicide!\n",op);
								break;
							}
							logMessage(stderr,"Could not remove Client: \"%s\", request was from user %s\n",op,users[i].ownerName);
							fprintf(users[i].streamFF,"%s\n",ANSWER_FAILURE);
						}else{
							if(users[i].streamFF == NULL){
								logMessage(stderr,"User %s successfully commited suicide!\n",op);
								break;
							}

							logMessage(stdout,"Succesfully removed Client \"%s\" , as requested from %s\n",op,users[i].ownerName);
							fprintf(users[i].streamFF,"%s\n",ANSWER_OK);
						}
					}
					fflush(users[i].streamFF);
				}

			}
		}

	}

}

inode* addVideo(char *name)
{
	int i;
	int idx = -1;
	int hadIt = 0;
	for(i = 0; i < MAXARG; i++)
	{
		if(list[i] == NULL)
		{
			idx = (idx==-1)?i:idx;
		}else if(strcmp(list[i]->name,name) == 0){
			free(list[i]->name);
			list[i]->name = NULL;
			free(list[i]);
			list[i] = NULL;
			idx = (idx==-1)?i:idx;
			hadIt = 1;
		}
	}
	if (idx == -1){
		logMessage(stdout,"The HardDisk is out of space, \"%s\" could not be saved\n",name);
		return NULL;
	}else{
		list[idx] = (inode*)malloc(sizeof(inode));
		list[idx]->name = (char*) malloc(sizeof(char)*strlen(name));
		strcpy(list[idx]->name,name);
		list[idx]->modif_time = time(NULL);
	}
	if(hadIt){
		return NULL;
	}
	return list[i];
}

void getList(char** sendList)
{
	int i;

	*sendList = (char*)malloc((MAXARG*(MAXSIZE*2))*sizeof(char));
	(*sendList)[0] = '\0';
	for(i = 0; i < MAXARG; i++)
	{
		if(list[i] != NULL && list[i]->name != NULL)
		{
			strcat(*sendList, list[i]->name);
			strcat(*sendList, " ");
		}
	}
	(*sendList)[strlen(*sendList) - 1] = '\0';
}

void getClientList(char** sendList)
{
	int i;
	*sendList = (char*)malloc((nusersm*(MAXSIZE*2))*sizeof(char));
	(*sendList)[0] = '\0';
	for(i = 0; i < nusersm; i++)
	{
		if(users[i].ownerName != NULL)
		{
			strcat(*sendList, users[i].ownerName);
			strcat(*sendList, " ");
		}
	}
	(*sendList)[strlen(*sendList) - 1] = '\0';
}



int removeClient(char* name){
	int i, isRemoved = 0;
	/** do not kill ROOT **/
	for(i = 1; i < nusersm; i++)
	{
		if(users[i].ownerName != NULL)
		{
			if(strcmp(name,users[i].ownerName) == 0){ /* equal */
				free(users[i].ownerName);
				users[i].ownerName = NULL;
				isRemoved = 1;
				fclose(users[i].streamFF);
				fclose(users[i].streamTF);
				close( users[i].tuFF[TU_WRITE]);
				close( users[i].tuTF[TU_READ]); 
				kill(users[i].ownerId,SIGTERM);
				users[i].streamTF = NULL;
				users[i].streamFF = NULL;
			}
		}
	}

	if (isRemoved == 0){
		logMessage(stderr,"There was no Client named \"%s\" could not be deleted\n",name);
	}

	return isRemoved;
}




int removeVideo(char* name){
	int i, isRemoved = 0;
	for(i = 0; i < MAXARG; i++)
	{
		if(list[i] != NULL)
		{
			if(strcmp(name,list[i]->name) == 0){ /* equal */
				free(list[i]->name);
				list[i]->name = NULL;
				free(list[i]);
				list[i] = NULL;
				isRemoved = 1;
				/*break;*/

			}
		}
	}

	if (isRemoved == 0){
		logMessage(stderr,"There was no file named \"%s\" could not be deleted\n",name);
	}
	return isRemoved;
}


void initFileSystem(){
	int i;
	for(i = 0;i<MAXARG;i++){
		list[i] = NULL;
	}
}
